package cn.aws360.boot.system.biz.service.impl;

import cn.aws360.boot.base.api.constant.enums.DelFlagEnum;
import cn.aws360.boot.base.api.exception.ServiceException;
import cn.aws360.boot.data.api.constant.DataConstant;
import cn.aws360.boot.data.api.factory.PageFactory;
import cn.aws360.boot.data.api.pojo.request.PageQuery;
import cn.aws360.boot.data.api.pojo.response.PageResult;
import cn.aws360.boot.data.api.utils.PojoUtils;
import cn.aws360.boot.system.api.constant.SysConstant;
import cn.aws360.boot.system.api.exception.SysExceptionEnum;
import cn.aws360.boot.system.api.pojo.request.SysMenuQuery;
import cn.aws360.boot.system.api.pojo.request.SysMenuWrite;
import cn.aws360.boot.system.api.pojo.response.SysMenuModel;
import cn.aws360.boot.system.biz.domain.SysMenu;
import cn.aws360.boot.system.biz.domain.SysRoleMenu;
import cn.aws360.boot.system.biz.helper.TreeHelper;
import cn.aws360.boot.system.biz.mapper.SysMenuMapper;
import cn.aws360.boot.system.biz.service.SysMenuService;
import cn.aws360.boot.system.biz.service.SysRoleMenuService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author junnan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    private final SysRoleMenuService roleMenuService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_MENU_CACHE, allEntries = true)
    public Boolean add(SysMenuWrite sysMenuWrite) {
        Integer pid = sysMenuWrite.getPid();
        sysMenuWrite.setPid(ObjectUtil.isNull(pid) ? 0 : pid);
        SysMenu sysMenu = BeanUtil.copyProperties(sysMenuWrite, SysMenu.class, DataConstant.ID);
        boolean flag = this.save(sysMenu);
        if (flag) {
            SysRoleMenu rootRoleMenu = SysRoleMenu.builder().roleId(SysConstant.ROOT_ID).menuId(sysMenu.getId()).build();
            return roleMenuService.save(rootRoleMenu);
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_MENU_CACHE, allEntries = true)
    public Boolean edit(SysMenuWrite sysMenuWrite) {
        Integer menuId = sysMenuWrite.getId();
        checkSysDontUpdate(menuId);
        Integer pid = sysMenuWrite.getPid();
        sysMenuWrite.setPid(ObjectUtil.isNull(pid) ? 0 : pid);
        SysMenu sysMenu = this.getSysMenuById(menuId);
        BeanUtil.copyProperties(sysMenuWrite, sysMenu);
        return this.updateById(sysMenu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysConstant.MODULE_MENU_CACHE, allEntries = true)
    public Boolean remove(String ids) {
        List<Integer> menuIdList = Convert.toList(Integer.class, ids.split(StrUtil.COMMA));
        menuIdList.forEach(this::checkSysDontDelete);
        this.listByIds(menuIdList).forEach(this::checkHasItemDontDelete);
        LambdaQueryWrapper<SysRoleMenu> queryWrapper = Wrappers.<SysRoleMenu>lambdaQuery().in(SysRoleMenu::getMenuId, menuIdList);
        roleMenuService.getBaseMapper().delete(queryWrapper);
        return this.removeByIds(menuIdList);
    }

    @Override
    public SysMenuModel detail(Integer id) {
        SysMenu sysMenu = this.getSysMenuById(id);
        return BeanUtil.copyProperties(sysMenu, SysMenuModel.class);
    }

    @Override
    public List<SysMenuModel> findList(SysMenuQuery sysMenuQuery) {
        LambdaQueryWrapper<SysMenu> queryWrapper = createQueryWrapper(sysMenuQuery);
        List<SysMenu> sysMenuList = this.list(queryWrapper);
        return PojoUtils.copyToList(sysMenuList, SysMenuModel.class);
    }

    @Override
    public PageResult<SysMenuModel> findPage(SysMenuQuery sysMenuQuery, PageQuery pageQuery) {
        LambdaQueryWrapper<SysMenu> queryWrapper = createQueryWrapper(sysMenuQuery);
        PageResult<SysMenu> sysMenuPage = PageFactory.getPageResult(this, pageQuery, queryWrapper);
        return PojoUtils.copyToPage(sysMenuPage, SysMenuModel.class);
    }

    @Override
    public List<Tree<Integer>> findTree(SysMenuQuery sysMenuQuery) {
        LambdaQueryWrapper<SysMenu> queryWrapper = createQueryWrapper(sysMenuQuery);
        List<SysMenu> sysMenuList = this.list(queryWrapper);

        // TODO 菜单数据权限
        List<TreeNode<Integer>> menuNodeList = TreeHelper.buildMenuTreeNodeList(sysMenuList);

        if (StrUtil.isNotBlank(sysMenuQuery.getName())) {
            return TreeHelper.buildTreeList(menuNodeList);
        }
        return TreeUtil.build(menuNodeList, 0);
    }

    private SysMenu getSysMenuById(Integer id) {
        SysMenu sysMenu = this.getById(id);
        checkNotExist(sysMenu);
        return sysMenu;
    }

    private LambdaQueryWrapper<SysMenu> createQueryWrapper(SysMenuQuery menuQuery) {
        return Wrappers.<SysMenu>lambdaQuery()
                .eq(ObjectUtil.isNotNull(menuQuery.getType()), SysMenu::getType, menuQuery.getType())
                .like(ObjectUtil.isNotEmpty(menuQuery.getName()), SysMenu::getName, menuQuery.getName())
                .ne(ObjectUtil.isNotNull(menuQuery.getFilterAllSubId()), SysMenu::getId, menuQuery.getFilterAllSubId())
                .orderByAsc(SysMenu::getSort);
    }


    private void checkNotExist(SysMenu sysMenu) {
        if (ObjectUtil.isNull(sysMenu) || DelFlagEnum.Y.getValue().equals(sysMenu.getDelFlag())) {
            throw new ServiceException(SysExceptionEnum.MENU_NOT_EXIST);
        }
    }

    private void checkSysDontUpdate(Integer menuId) {
        if (SysConstant.ROOT_ID.equals(menuId)) {
            throw new ServiceException(SysExceptionEnum.MENU_DONT_UPDATE_FOR_ROOT);
        }
    }

    private void checkSysDontDelete(Integer menuId) {
        if (SysConstant.ROOT_ID.equals(menuId)) {
            throw new ServiceException(SysExceptionEnum.MENU_DONT_DELETE_FOR_ROOT);
        }
    }

    private void checkHasItemDontDelete(SysMenu sysMenu) {
        Integer menuId = sysMenu.getId();
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getPid, menuId);
        List<SysMenu> sysMenuList = this.list(queryWrapper);
        if (CollUtil.isNotEmpty(sysMenuList)) {
            throw new ServiceException(SysExceptionEnum.MENU_DONT_DELETE_FOR_HAS_ITEM, menuId);
        }
    }

}
